<!--
 * Copyright 2009, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>WebGL Vertex Compression example</title>
<style>
html, body {
  width: 100%;
  height: 100%;
  border: 0px;
  padding: 0px;
  margin: 0px;
  background-color: red;
}
CANVAS {
  background-color: gray;
}
.fpsContainer {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 3;
  color: white;
  font-family: sans-serif;
  background-color: rgba(0,0,0,0.5);
  border-radius: 10px;
  padding: 10px;
}
#viewContainer {
  width: 100%;
  height: 100%;
}
#labels {
  z-index: 2;
  position: absolute;
  top: 0px,
  left: 0px;
  width: 100%;
  height: 100%;
}
#labels TD {
  width: 33%;
  vertical-align: bottom;
  text-align: center;
}
#labels DIV {
  color: white;
  background-color: rgba(0,0,0,0.5);
  border-radius: 10px;
  padding: 5px;
  margin: 5px;
}
</style>
<script src="../tdl/base.js"></script>
<script>
tdl.require('tdl.buffers');
tdl.require('tdl.fast');
tdl.require('tdl.fps');
tdl.require('tdl.io');
tdl.require('tdl.log');
tdl.require('tdl.math');
tdl.require('tdl.models');
tdl.require('tdl.primitives');
tdl.require('tdl.programs');
tdl.require('tdl.textures');
tdl.require('tdl.webgl');
window.onload = initialize;

// globals
var gl;                   // the gl context.
var canvas;               // the canvas
var math;                 // the math lib.
var fast;                 // the fast math lib.
var g_fpsTimer;           // object to measure frames per second;
var g_logGLCalls = true;  // whether or not to log webgl calls
var g_debug = false;      // whether or not to debug.
var g_drawOnce = false;   // draw just one frame.
var g_info;

//g_drawOnce = true;
//g_debug = true;

var g_eyeSpeed          = 0.0;
var g_eyeHeight         = 0;
var g_eyeRadius         = 3;
var g_speed             = 0.2;
var g_transOff          = [-0.5, -3, -5.5];

Decoder = function() {
  var buf = new ArrayBuffer(4);
  this.float_ = new Float32Array(buf);
  this.uint16_ = new Uint16Array(buf);
  this.uint32_ = new Uint32Array(buf);
  this.half_ = false;
  this.highByte_ = 0;
};

Decoder.prototype.getInt16 = function() {
  var v = this.getUint16();
  if (v > 0x7fff) {
    v = v - 0x10000;
  }
  return v;
};

Decoder.prototype.getInt8 = function() {
  var v = this.getUint8();
  if (v > 0x7f) {
    v = v - 0x100;
  }
  return v;
};

Decoder.prototype.getInt16Float = function() {
  var v = this.getInt16();
  return v / 0x7FFF;
};

Decoder.prototype.getUint16Float = function() {
  var v = this.getUint16();
  return v / 0xFFFF;
};

Decoder.prototype.getUint32 = function() {
  // TODO(gman): fix. Currently Uint32 must be on 2 byte boundry.
  this.uint16_[0] = this.getUint16();
  this.uint16_[1] = this.getUint16();
  return this.uint32_[0];
};

Decoder.prototype.getFloat = function() {
  // TODO(gman): fix. Currently float must be on 2 byte boundry.
  var l = this.getUint16();
  var h = this.getUint16();
  this.uint16_[0] = l;
  this.uint16_[1] = h;
  return this.float_[0];
};

Decoder.prototype.getUint8 = function() {
  if (!this.half_) {
    var v = this.getUint16();
    this.highByte_ = v >> 8;
    this.half_ = true;
    return v & 0xFF;
  } else {
    this.half_ = false;
    return this.highByte_;
  }
}

UTF8Decoder = function(str) {
  Decoder.call(this);
  this.str_ = str;
  this.ndx_ = 0;
  this.prev_ = 0;
};

tdl.base.inherit(UTF8Decoder, Decoder);

UTF8Decoder.prototype.getUint16 = function() {
  if (this.half_) {
    throw("can only get 16 bit values from 2 byte boundries");
  }
  var word = this.str_.charCodeAt(this.ndx_++);
  word = ((word >> 1) | ((word & 0x1) << 15));
  var next = (this.prev_ + word) & 0xFFFF;
  this.prev_ = next;
  return next;
};

ArrayBufferDecoder = function(buf) {
  Decoder.call(this);
  this.buf_ = new Uint16Array(buf);
  this.ndx_ = 0;
  this.prev_ = 0;
};

tdl.base.inherit(ArrayBufferDecoder, Decoder);

ArrayBufferDecoder.prototype.getUint16 = function() {
  if (this.half_) {
    throw("can only get 16 bit values from 2 byte boundries");
  }
  this.prev_ = (this.buf_[this.ndx_++] + this.prev_) & 0xFFFF;
  return this.prev_;
}

function ValidateNoneOfTheArgsAreUndefined(functionName, args) {
  for (var ii = 0; ii < args.length; ++ii) {
    if (args[ii] === undefined) {
      tdl.error("undefined passed to gl." + functionName + "(" +
                tdl.webgl.glFunctionArgsToString(functionName, args) + ")");
    }
  }
}

function Log(msg) {
  if (g_logGLCalls) {
    tdl.log(msg);
  }
}

function LogGLCall(functionName, args) {
  if (g_logGLCalls) {
    ValidateNoneOfTheArgsAreUndefined(functionName, args)
    tdl.log("gl." + functionName + "(" +
                tdl.webgl.glFunctionArgsToString(functionName, args) + ")");
  }
}

/**
 * Sets up Planet.
 */
function setupSphere() {
  var textures = {
    diffuseSampler: tdl.textures.loadTexture('sometexture.png')};
  var program = tdl.programs.loadProgramFromScriptTags(
      'sphereVertexShader',
      'sphereFragmentShader');
  var arrays = tdl.primitives.createSphere(1, 100, 100);

  return new tdl.models.Model(program, arrays, textures);
}

function setup32BytePerVertexModel(decoder) {
  var numVertices = decoder.getUint16();
  var numTriangles = decoder.getUint16();

  tdl.log("--32 byte per--");
  tdl.log("num verts: ", numVertices);
  tdl.log("num tris : ", numTriangles);

  var positions = new tdl.primitives.AttribBuffer(3, numVertices , 'Float32Array');
  var normals = new tdl.primitives.AttribBuffer(3, numVertices , 'Float32Array');
  var uvs = new tdl.primitives.AttribBuffer(2, numVertices , 'Float32Array');
  var indices = new tdl.primitives.AttribBuffer(3, numTriangles, 'Uint16Array');

  for (var ii = 0; ii < numVertices; ++ii) {
    positions.setElement(ii, [decoder.getFloat(), decoder.getFloat(), decoder.getFloat()]);
  }
  for (var ii = 0; ii < numVertices; ++ii) {
    normals.setElement(ii, [decoder.getFloat(), decoder.getFloat(), decoder.getFloat()]);
    if (ii < 10) {
      var v = normals.getElement(ii);
      tdl.log(ii, v[0], v[1], v[2]);
    }
  }
  for (var ii = 0; ii < numVertices; ++ii) {
    uvs.setElement(ii, [decoder.getFloat(), decoder.getFloat()]);
  }
  for (var ii = 0; ii < numTriangles; ++ii) {
    indices.setElement(ii, [decoder.getUint16(), decoder.getUint16(), decoder.getUint16()]);
  }

  var textures = {
    diffuseSampler: tdl.textures.loadTexture('sometexture.png')};
  var program = tdl.programs.loadProgramFromScriptTags(
      'sphereVertexShader',
      'sphereFragmentShader');
  var arrays = {
    position: positions,
    normal: normals,
    texCoord: uvs,
    indices: indices};

  return {
    model: new tdl.models.Model(program, arrays, textures),
    positionMin: [0,0,0],
    positionScale: [1,1,1]
  };
}

function setup16BytePerVertexModel(decoder) {
  var numVertices = decoder.getUint16();
  var numTriangles = decoder.getUint16();
  var positionScale = [
      decoder.getFloat(), decoder.getFloat(), decoder.getFloat()];

  tdl.log("--16 byte per--");
  tdl.log("num verts: ", numVertices);
  tdl.log("num tris : ", numTriangles);
  tdl.log("pos scale: ", positionScale[0], positionScale[1], positionScale[2]);

  var positions = new tdl.primitives.AttribBuffer(3, numVertices , 'Int16Array');
  var normals = new tdl.primitives.AttribBuffer(3, numVertices , 'Int16Array');
  var uvs = new tdl.primitives.AttribBuffer(2, numVertices , 'Int16Array');
  var indices = new tdl.primitives.AttribBuffer(3, numTriangles, 'Uint16Array');

  // We should just pass position scale to the shader.
  for (var ii = 0; ii < numVertices; ++ii) {
    positions.setElement(ii, [
        decoder.getInt16() * 2,
        decoder.getInt16() * 2,
        decoder.getInt16() * 2]);
    if (ii < 10) {
      var v = positions.getElement(ii);
      tdl.log("p", ii,
              positionScale[0] * (v[0] / 0x7FFF),
              positionScale[1] * (v[1] / 0x7FFF),
              positionScale[2] * (v[2] / 0x7FFF));
    }
  }
  for (var ii = 0; ii < numVertices; ++ii) {
    normals.setElement(ii, [
        decoder.getInt16() * 64,
        decoder.getInt16() * 64,
        decoder.getInt16() * 64]);
    if (ii < 10) {
      var v = normals.getElement(ii);
      tdl.log("n", ii, v[0] / 0x7FFF, v[1] / 0x7FFF, v[2] / 0x7FFF);
    }
  }
  for (var ii = 0; ii < numVertices; ++ii) {
    uvs.setElement(ii, [
        decoder.getInt16() * 64,
        decoder.getInt16() * 64]);
  }
  for (var ii = 0; ii < numTriangles; ++ii) {
    indices.setElement(ii, [decoder.getUint16(), decoder.getUint16(), decoder.getUint16()]);
  }

  var textures = {
    diffuseSampler: tdl.textures.loadTexture('sometexture.png')};
  var program = tdl.programs.loadProgramFromScriptTags(
      'sphereVertexShader',
      'sphereFragmentShader');
  var arrays = {
    position: positions,
    normal: normals,
    texCoord: uvs,
    indices: indices};

  return {
    model: new tdl.models.Model(program, arrays, textures),
    positionMin: [0,0,0],
    positionScale: positionScale
  };
}

function setup9BytePerVertexModel(decoder) {
  var numVertices = decoder.getUint16();
  var numTriangles = decoder.getUint16();
  var positionMin = [
      decoder.getFloat(), decoder.getFloat(), decoder.getFloat()];
  var positionScale = [
      decoder.getFloat(), decoder.getFloat(), decoder.getFloat()];

  tdl.log("--9 byte per--");
  tdl.log("num verts: ", numVertices);
  tdl.log("num tris : ", numTriangles);

  tdl.log("pos min: ", positionMin[0], positionMin[1], positionMin[2]);
  tdl.log("pos scale: ", positionScale[0], positionScale[1], positionScale[2]);

  var positions = new tdl.primitives.AttribBuffer(3, numVertices , 'Float32Array');
  var normals = new tdl.primitives.AttribBuffer(3, numVertices , 'Int8Array');
  var uvs = new tdl.primitives.AttribBuffer(2, numVertices , 'Uint8Array');
  var indices = new tdl.primitives.AttribBuffer(3, numTriangles, 'Uint16Array');

  for (var ii = 0; ii < numVertices; ++ii) {
    var l = decoder.getUint16();
    var h = decoder.getUint16();
    positions.setElement(ii, [
        (h >> 5) / 0x7FF,
        (((h & 0x1F) << 5) | ((l >> 11) & 0x1f)) / 0x3FF,
        (l & 0x7FF) / 0x7FF]);
    if (ii < 10) {
      var v = positions.getElement(ii);
      tdl.log("p", ii,
              positionMin[0] + positionScale[0] * v[0],
              positionMin[1] + positionScale[1] * v[1],
              positionMin[2] + positionScale[2] * v[2]);
    }
  }
  for (var ii = 0; ii < numVertices; ++ii) {
    normals.setElement(ii, [decoder.getInt8(), decoder.getInt8(), decoder.getInt8()]);
    if (ii < 10) {
      var v = normals.getElement(ii);
      tdl.log("n", ii, v[0] / 0x7f, v[1] / 0x7f, v[2] / 0x7f);
    }
  }
  if (numVertices % 2) {
    tdl.log("eat one");
    decoder.getUint8();
  }
  for (var ii = 0; ii < numVertices; ++ii) {
    uvs.setElement(ii, [decoder.getUint8(), decoder.getUint8()]);
  }
  for (var ii = 0; ii < numTriangles; ++ii) {
    indices.setElement(ii, [decoder.getUint16(), decoder.getUint16(), decoder.getUint16()]);
    if (ii < 10) {
      var v = indices.getElement(ii);
      tdl.log("i", ii, v[0], v[1], v[2]);
    }
  }

  var textures = {
    diffuseSampler: tdl.textures.loadTexture('sometexture.png')};
  var program = tdl.programs.loadProgramFromScriptTags(
      'sphereVertexShader',
      'sphereFragmentShader');
  var arrays = {
    position: positions,
    normal: normals,
    texCoord: uvs,
    indices: indices};

  return {
    model: new tdl.models.Model(program, arrays, textures),
    positionMin: positionMin,
    positionScale: positionScale
  };
}

function compareUTF8ToBinary() {
  tdl.io.loadTextFile("bytes16.utf8", function(b1, e) {
    if (e) {
      alert(e);
    } else {
      tdl.io.loadArrayBuffer("bytes16.bin", function(b2, e) {
        if (e) {
          alert(e);
        } else {
          var u = new UTF8Decoder(b1);
          var b = new ArrayBufferDecoder(b2);
          var c = 0;
          for (var ii = 0; ii < b1.length; ++ii) {
            var uv = u.getUint16();
            var bv = b.getUint16();
            if (uv != bv) {
              tdl.log(ii, " ", uv.toString(16), " ", bv.toString(16));
              ++c;
              if (c > 100) {
                break;
              }
            }
          }
        }
      });
    }
  });
}

function initialize() {
  math = tdl.math;
  fast = tdl.fast;
  canvas = document.getElementById("canvas");
  g_fpsTimer = new tdl.fps.FPSTimer();

  gl = tdl.webgl.setupWebGL(canvas);
  if (!gl) {
    return false;
  }
  if (g_debug) {
    gl = tdl.webgl.makeDebugContext(gl, undefined, LogGLCall);
  }

  Log("--Setup Sphere---------------------------------------");
  var sphere = setupSphere();
  var info = {
    model: sphere,
    positionMin: [0,0,0],
    positionScale: [1,1,1],
  };
  var models = [info, info, info, info, info, info];
  g_info = models;

  //compareUTF8ToBinary();

  function utf8Loader(index, url, decoder) {
    tdl.io.loadTextFile(url, function(buf, exception) {
        if (exception) {
          alert(exception);
        } else {
          models[index] = decoder(new UTF8Decoder(buf));
        }
      });
  }

  function binLoader(index, url, decoder) {
    tdl.io.loadArrayBuffer(url, function(buf, exception) {
        if (exception) {
          alert(exception);
        } else {
          models[index] = decoder(new ArrayBufferDecoder(buf));
        }
      });
  }

  var loaders = [
    { url: "bytes16.utf8",
      loader: utf8Loader,
      decoder: setup16BytePerVertexModel
    },
    { url: "bytes9.utf8",
      loader: utf8Loader,
      decoder: setup9BytePerVertexModel
    },
    { url: "bytes32.utf8",
      loader: utf8Loader,
      decoder: setup32BytePerVertexModel
    },
    { url: "bytes16.bin",
      loader: binLoader,
      decoder: setup16BytePerVertexModel
    },
    { url: "bytes9.bin",
      loader: binLoader,
      decoder: setup9BytePerVertexModel
    },
    { url: "bytes32.bin",
      loader: binLoader,
      decoder: setup32BytePerVertexModel
    }
  ];

  for (var ll = 0; ll < loaders.length; ++ll) {
    var loader = loaders[ll];
    if (loader.loader) {
      loader.loader(ll, loader.url, loader.decoder);
    }
  }

  var then = 0.0;
  var clock = 0.0;
  var fpsElem = document.getElementById("fps");

  // pre-allocate a bunch of arrays
  var projection = new Float32Array(16);
  var view = new Float32Array(16);
  var world = new Float32Array(16);
  var worldInverse = new Float32Array(16);
  var worldInverseTranspose = new Float32Array(16);
  var viewProjection = new Float32Array(16);
  var worldViewProjection = new Float32Array(16);
  var viewInverse = new Float32Array(16);
  var viewProjectionInverse = new Float32Array(16);
  var eyePosition = new Float32Array(3);
  var target = new Float32Array(3);
  var up = new Float32Array([0,1,0]);
  var lightWorldPos = new Float32Array(3);
  var v3t0 = new Float32Array(3);
  var v3t1 = new Float32Array(3);
  var v3t2 = new Float32Array(3);
  var v3t3 = new Float32Array(3);
  var m4t0 = new Float32Array(16);
  var m4t1 = new Float32Array(16);
  var m4t2 = new Float32Array(16);
  var m4t3 = new Float32Array(16);
  var zero4 = new Float32Array(4);
  var one4 = new Float32Array([1,1,1,1]);

  // Model uniforms.
  var modelConst = {
    viewInverse: viewInverse,
    lightWorldPos: lightWorldPos,
    specular: one4,
    shininess: 50,
    specularFactor: 0.2};
  var modelPer = {
    lightColor: new Float32Array([0.8, 0.9, 1, 1]),
    world: world,
    worldViewProjection: worldViewProjection,
    worldInverse: worldInverse,
    worldInverseTranspose: worldInverseTranspose};

  var frameCount = 0;
  var intervalId = setInterval(render, 1000.0 / 70.0);
  function render() {
    ++frameCount;
    if (g_drawOnce) {
      clearInterval(intervalId);
    }
    var now = (new Date()).getTime() * 0.001;
    var elapsedTime;
    if(then == 0.0) {
      elapsedTime = 0.0;
    } else {
      elapsedTime = now - then;
    }
    then = now;

    g_fpsTimer.update(elapsedTime);
    fpsElem.innerHTML = g_fpsTimer.averageFPS;

    clock += elapsedTime;
    eyePosition[0] = Math.sin(clock * g_eyeSpeed) * g_eyeRadius;
    eyePosition[1] = g_eyeHeight;
    eyePosition[2] = Math.cos(clock * g_eyeSpeed) * g_eyeRadius;

    var pWidth = canvas.clientWidth / 3;
    var pHeight = canvas.clientHeight / 2;
    fast.matrix4.perspective(
        projection,
        math.degToRad(60),
        pWidth / pHeight,
        1,
        5000);
    fast.matrix4.lookAt(
        view,
        eyePosition,
        target,
        up);
    fast.matrix4.mul(viewProjection, view, projection);
    fast.matrix4.inverse(viewInverse, view);
    fast.matrix4.inverse(viewProjectionInverse, viewProjection);

    fast.matrix4.getAxis(v3t0, viewInverse, 0); // x
    fast.matrix4.getAxis(v3t1, viewInverse, 1); // y;
    fast.matrix4.getAxis(v3t2, viewInverse, 2); // z;
    fast.mulScalarVector(v3t0, 10, v3t0);
    fast.mulScalarVector(v3t1, 10, v3t1);
    fast.mulScalarVector(v3t2, 10, v3t2);
    fast.addVector(lightWorldPos, eyePosition, v3t0);
    fast.addVector(lightWorldPos, lightWorldPos, v3t1);
    fast.addVector(lightWorldPos, lightWorldPos, v3t2);

    gl.enable(gl.CULL_FACE);
    gl.enable(gl.DEPTH_TEST);
    gl.enable(gl.SCISSOR_TEST);

    for (var ii = 0; ii < 6; ++ii) {
      var vx = ii % 3;
      var vy = Math.floor(ii / 3);
      var vWidth = Math.floor(canvas.width / 3);
      var vHeight = Math.floor(canvas.height / 2);
      gl.viewport(vWidth * vx, vHeight * vy, vWidth, vHeight);
      gl.scissor(vWidth * vx, vHeight * vy, vWidth - 1, vHeight - 1);
      gl.colorMask(true, true, true, true);
      gl.depthMask(true);
      gl.clearColor(0,0,1,1);
      gl.clearDepth(1);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);

  //      view: view,
  //      projection: projection,
  //      viewProjection: viewProjection,

      Log("--Draw model---------------------------------------");
      var lightColor = modelPer.lightColor;
      var info = models[ii];
      var model = info.model;
      if (!model) {
        continue;
      }
      modelConst.positionMin = info.positionMin;
      modelConst.positionScale = info.positionScale;
      model.drawPrep(modelConst);
      fast.matrix4.rotationY(m4t0, clock * g_speed);
      fast.matrix4.translation(m4t1, g_transOff);
      fast.matrix4.mul(world, m4t1, m4t0);
      fast.matrix4.mul(worldViewProjection, world, viewProjection);
      fast.matrix4.inverse(worldInverse, world);
      fast.matrix4.transpose(worldInverseTranspose, worldInverse);
      model.draw(modelPer);
    }

    // Set the alpha to 255.
    gl.disable(gl.SCISSOR_TEST);
    gl.colorMask(false, false, false, true);
    gl.clearColor(0,0,0,1);
    gl.clear(gl.COLOR_BUFFER_BIT);

    // turn off logging after 1 frame.
    g_logGLCalls = false;
  }
  return true;
}
</script>
</head>
<body>
<div class="fpsContainer">
  <div class="fps">fps: <span id="fps"></div>
</div>
<div id="viewContainer">
<table id="labels">
<tr><td><div>16 bytes bin</div></td><td><div>9 bytes bin</div></td><td><div>32 bytes bin</div></td></tr>
<tr><td><div>16 bytes utf8</div></td><td><div>9 bytes utf8</div></td><td><div>32 bytes utf8</div></td></tr>
</table>
<canvas id="canvas" width="1024" height="1024" style="width: 100%; height: 100%;"></canvas>
</div>
</body>
<script id="sphereVertexShader" type="text/something-not-javascript">
uniform mat4 worldViewProjection;
uniform vec3 lightWorldPos;
uniform mat4 world;
uniform mat4 viewInverse;
uniform mat4 worldInverseTranspose;
uniform vec3 positionMin;
uniform vec3 positionScale;
attribute vec4 position;
attribute vec3 normal;
attribute vec2 texCoord;
varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
void main() {
  v_texCoord = texCoord;
  vec4 pos = vec4(positionMin, 1) + position * vec4(positionScale, 1);
  v_position = (worldViewProjection * pos);
  v_normal = (worldInverseTranspose * vec4(normal, 0)).xyz;
  v_surfaceToLight = lightWorldPos - (world * pos).xyz;
  v_surfaceToView = (viewInverse[3] - (world * pos)).xyz;
  gl_Position = v_position;
}

</script>
<script id="sphereFragmentShader" type="text/something-not-javascript">
#ifdef GL_FRAGMENT_PRECISION_HIGH
  precision highp float;
#else
  precision mediump float;
#endif
uniform vec4 lightColor;
varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;

uniform sampler2D diffuseSampler;
uniform vec4 specular;
uniform sampler2D bumpSampler;
uniform float shininess;
uniform float specularFactor;

vec4 lit(float l ,float h, float m) {
  return vec4(1.0,
              max(l, 0.0),
              (l > 0.0) ? pow(max(0.0, h), m) : 0.0,
              1.0);
}
void main() {
  vec4 diffuse = texture2D(diffuseSampler, v_texCoord);
  vec3 normal = normalize(v_normal);
  vec3 surfaceToLight = normalize(v_surfaceToLight);
  vec3 surfaceToView = normalize(v_surfaceToView);
  vec3 halfVector = normalize(surfaceToLight + surfaceToView);
  vec4 litR = lit(dot(normal, surfaceToLight),
                    dot(normal, halfVector), shininess);
  gl_FragColor = vec4((
  lightColor * (diffuse * litR.y
                        + specular * litR.z * specularFactor)).rgb,
      diffuse.a);
}
</script>
</html>



